package dk.dashboardFP.dfpLogger {
	import dk.dashboardFP.panels.loggerPanel.InspectDCLogItem;
	import dk.dashboardFP.dfpLogger.logReceivers.ILogReceiver;

	import flash.display.DisplayObject;
	import flash.events.EventDispatcher;

	/**
	 * @author josef
	 */
	public class DFPLogger extends EventDispatcher {

		private static var _instance : DFPLogger;
		private static var _bCreatingInstance : Boolean = false;

		private var _o_oLogGroups : Object = {};
		private var _a_oLogReceivers : Array = [];

		public function DFPLogger() {
			if( !_bCreatingInstance ) throw new Error("Singleton and can only be accessed through DFPLogger.get_oInstance()");
			init();
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// init
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		private function init() : void {
			_getLogGroupById(-1).color = 0x999999;
			_getLogGroupById(-1).name = "Default";
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// log groups
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		public static function getLogGroupById(intGroup : int) : LogGroup {
			return DFPLogger.getInstance()._getLogGroupById(intGroup);
		}

		private function _getLogGroupById(intGroup : int) : LogGroup {
			if (!_o_oLogGroups[intGroup]) {
				_o_oLogGroups[intGroup] = new LogGroup(intGroup);
			}
			return _o_oLogGroups[intGroup];
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// interface
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		public static function log(oMessage : Object, intGroup : int = -1, oData : Object = null, intLevel : int = 0, oExtraReceiverParms : Object = null) : void {
			getInstance().doLog(oMessage.toString(), intGroup, oData, intLevel, oExtraReceiverParms);
		}

		public static function logStack(intGroup : int = -1, intLevel : int = 0) : void {
			
			try {
				throw new Error();	
			}catch(e : Error) {
				
				var sLogString : String = "";
				var a_sLogSegments : Array = e.getStackTrace().split("at ");
				a_sLogSegments.splice(0, 2);
				
				var sLastClassName : String = "";
				
				for each (var s : String in a_sLogSegments) {
				
					var sClassName : String = s.slice(s.indexOf("::") + 2, s.indexOf("/"));
					if (sClassName != sLastClassName) {
						
						if (sClassName != "EventDispatcher") {
							sLogString += sClassName + "\n";
						} else {
							sLogString += "<--- " + sClassName + " --->" + "\n";
							;
						}
						sLastClassName = sClassName;
					}
					
					if (sLastClassName != "EventDispatcher") {
						var sFunctionName : String = s.slice(s.indexOf("/") + 1, s.indexOf("()") + 2);
						sLogString += "       " + sFunctionName;
						
						if (s.indexOf("as:") != -1) {
							var sLineNumber : String;
							sLineNumber = s.slice(s.indexOf("as:") + 3, s.indexOf("]"));
							sLogString += "   Line: " + sLineNumber;
						}
						sLogString += "\n";
					}
				}
				
				//:::::::
				// remove end line breaks
				sLogString = sLogString.slice(0, sLogString.lastIndexOf(")") + 1); 
				
				//:::::::
				// do log 
				log(sLogString, intGroup, intLevel);
			}
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// introspction interaface
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		public static function inspect(message : String = "", object : Object = null, intGroup : int = -1, preSearch : String = "", autoOpen : Boolean = false, intLogLevel : int = 0) : void {
			_instance.doInspect(message, object, intGroup, preSearch, autoOpen, intLogLevel);
		}

		private function doInspect(message : String, object : Object, intGroup : int, preSearch : String, autoOpen : Boolean, intLogLevel : int) : void {
			
			//:::::::
			// escape log if group is hidden and bLogWhileHidden is set to false 
			if (!DFPLogger.getLogGroupById(intGroup).visible && !DFPLogger.getLogGroupById(intGroup).logWhileHidden ) return;
			
			var inspectLogItem : InspectLogItem = new InspectLogItem(message, intGroup, object, intLogLevel, preSearch, autoOpen);
			dispatchEvent(new DFPLoggerEvent(DFPLoggerEvent.LOG, inspectLogItem));
		}

		public static function inspectDisplayChain(inspectionRoot : DisplayObject, message : String = "", intGroup : int = -1, preSearch : String = "", autoOpen : Boolean = false, logLevel : int = 0) : void {
			_instance.doInspectDC(inspectionRoot, message, intGroup, preSearch, autoOpen, logLevel);
		}

		private function doInspectDC(inspectionRoot : DisplayObject, message : String = "", intGroup : int = -1, preSearch : String = "", autoOpen : Boolean = false, logLevel : int = 0) : void {

			//:::::::
			// escape log if group is hidden and bLogWhileHidden is set to false 
			if (!DFPLogger.getLogGroupById(intGroup).visible && !DFPLogger.getLogGroupById(intGroup).logWhileHidden ) return;
			
			var inspectLogItem : InspectDCLogItem = new InspectDCLogItem(message, intGroup, inspectionRoot, logLevel, preSearch, autoOpen);
			dispatchEvent(new DFPLoggerEvent(DFPLoggerEvent.LOG, inspectLogItem));
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// log dispatcher
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		private function doLog(sMessage : String, intGroup : int, oData : Object, intLevel : int, oExtraReceiverParms : Object) : void {
			
			//:::::::
			// escape log if group is hidden and bLogWhileHidden is set to false 
			if (!DFPLogger.getLogGroupById(intGroup).visible && !DFPLogger.getLogGroupById(intGroup).logWhileHidden ) return;
			
			// escape if log level is less than groups setting 
			if (DFPLogger.getLogGroupById(intGroup).level > intLevel && !DFPLogger.getLogGroupById(intGroup).logWhileHidden) return;
			
			var oLogItem : DFPLogItem = new DFPLogItem(sMessage, intGroup, oData, intLevel, oExtraReceiverParms);
			dispatchEvent(new DFPLoggerEvent(DFPLoggerEvent.LOG, oLogItem));
			
			//:::::::
			// pass on 
			if (getLogGroupById(oLogItem.intGroup).visible && DFPLogger.getLogGroupById(intGroup).level > intLevel ) {
				passLogToReceivers(oLogItem);
			}
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// recievers
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		
		//:::::::
		// static interface 
		public static function addLogReceiver(logReceiver : ILogReceiver) : void {
			getInstance().doAddLogReceiver(logReceiver);
		}

		private function doAddLogReceiver(logReceiver : ILogReceiver) : void {
			_a_oLogReceivers.push(logReceiver);
		}

		private function passLogToReceivers(logItem : DFPLogItem) : void {
			for each (var oLogReceiver : ILogReceiver in _a_oLogReceivers) {
				oLogReceiver.log(logItem.sMessage, logItem.oData, logItem.intLevel, logItem.oExtraReceiverParms);
			}
		}		

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// singleton enforcer
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		public static function getInstance() : DFPLogger {
			if (!_instance) {
				_bCreatingInstance = true;
				_instance = new DFPLogger();
				_bCreatingInstance = false;
			}
			return _instance;
		}
	}
}
